package de.mnit.basis.swt.element.steuer.rollbar.gruppe.baum;

import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.TreeItem;

import de.mnit.basis.daten.struktur.S_Folge;
import de.mnit.basis.daten.struktur.gruppe.Gruppe2;
import de.mnit.basis.daten.struktur.gruppe.Gruppe3;
import de.mnit.basis.daten.struktur.liste.Liste;
import de.mnit.basis.daten.struktur.liste.S_Liste;
import de.mnit.basis.daten.struktur.schluessel.S_SchluesselMenge;
import de.mnit.basis.daten.struktur.schluessel.SchluesselMenge;
import de.mnit.basis.daten.struktur.tabelle.S_Tabelle;
import de.mnit.basis.daten.struktur.tabelle.Tabelle;
import de.mnit.basis.fehler.Fehler;
import de.mnit.basis.fehler.system.F_Sys_Datei;
import de.mnit.basis.swt.dialog.SWT_Fehler;
import de.mnit.basis.swt.dnd.A_DND_Quelle;
import de.mnit.basis.swt.dnd.A_DND_Ziel;
import de.mnit.basis.swt.dnd.DNDART;
import de.mnit.basis.swt.dnd.DNDTYP;
import de.mnit.basis.swt.dnd.transfer.IDObjekt;
import de.mnit.basis.swt.element.feld.baum.SWT_BaumFeld;
import de.mnit.basis.swt.element.feld.baum.S_BaumFeld;
import de.mnit.basis.swt.element.steuer.A_SWT_Steuer;
import de.mnit.basis.swt.grafik.S_Bild_SWT;
import de.mnit.basis.sys.Ausgabe;
import de.mnit.basis.sys.datei.JarZugriff;
import de.mnit.basis.sys.event.S_Extern;
import de.mnit.basis.sys.event.S_Zuhoerer;


/**
 * @author Michael Nitsche
 * 21.04.2010	Erstellt
 */
public class SWT_Baum_N2M extends A_SWT_Baum<SWT_Baum_N2M> {

	private S_SchluesselMenge<Integer,SWT_BaumFeld> id_map=SchluesselMenge.neu();
	private S_Extern<Gruppe3<Integer,Integer, Integer>,Boolean> externVerschieben=null;
	private S_Extern<Gruppe2<SWT_BaumFeld,IDObjekt[]>,Boolean> externDrop=null;
	private S_Zuhoerer<S_Folge<Ordner>> zVaterUngueltig=null;


	public S_Liste<SWT_BaumFeld> gAusgewaehlt() { //TODO In die Abstraktion?
		TreeItem[] auswahl=swt().getSelection();
		S_Liste<SWT_BaumFeld> erg=Liste.neu();
		for(TreeItem ti : auswahl)
			erg.plus((SWT_BaumFeld)ti.getData());
		return erg;
	}

	public boolean istFreigegeben() {
		return this.swt().isDisposed();
	}

	public void sExternVerschieben(S_Extern<Gruppe3<Integer, Integer, Integer>, Boolean> e) {
		this.externVerschieben=e;
	}

	public void sExternDrop(S_Extern<Gruppe2<SWT_BaumFeld,IDObjekt[]>,Boolean> e) {
		this.externDrop=e;
	}

	public void eVaterUngueltig(S_Zuhoerer<S_Folge<Ordner>> z) {
		this.zVaterUngueltig=z;
	}

//	public void sOffen(TypTabelle2<Integer,Boolean> tab) {
//		for(Gruppe2<Integer,Boolean> z : tab) {
//			boolean offen=z.g2();
//			SWT_BaumFeld ast=this.id_map.gib(z.g1());
//			if(offen)
//				ast.oeffnen();
//			else
//				ast.schliessen();
//		}
//	}

	public void sHintergrund(int id, Color c) {
		this.id_map.gib((Integer)id).swt().setBackground(c);
	}

	public boolean plus(Ordner o) {
		SWT_BaumFeld feld;
		if(o.gVater()==null)
			feld=this.nBaumFeld(o.text);
		else {
			if(!this.id_map.gSchluessel().kennt(o.gVater()))
				return false;
			else {
				SWT_BaumFeld vater_feld=id_map.gib(o.gVater());
				feld=vater_feld.nBaumFeld(o.text);
				vater_feld.oeffnen();
			}
		}
		feld.schluessel.s(o.id);
		this.id_map.plus(o.id, feld);

		//Bild setzen
		if(o.bild!=null)
			try {
				feld.sBild(JarZugriff.gJarStream(o.bild));
			}
		catch(F_Sys_Datei e) {
			SWT_Fehler.zeig(e, false);
		}

		//Öffnen //Funzt bei mehreren nicht!
		if(o.offen) feld.oeffnen();
		else 		feld.schliessen();

		return true;
	}

	public boolean minus(int id) {
		if(!this.id_map.gSchluessel().kennt(id))
			return false;
		SWT_BaumFeld feld=id_map.gib((Integer)id);
		feld.freigeben();
		this.id_map.minus(id);
		return true;
	}

	public void sAuswahl(int id) {
		TreeItem item=t.id_map.gib((Integer)id).swt();
		t.swt().setSelection(item);
	}

	public void setze(S_Folge<Ordner> ordner) {
		S_Folge<Ordner> kopie=ordner;
		if(this.id_map.gLaenge()>0) {
//			Fehler.sonstige.da_ToDo("Clean");
			this.id_map=SchluesselMenge.neu();
//			t.swt().clearAll(true);
			t.swt().removeAll();
		}

		this.id_map=SchluesselMenge.neu();

		while(ordner.gLaenge()>0) {
			S_Liste<Ordner> tab_neu=Liste.neu();
			for(Ordner z : ordner) {
				if(!plus(z))
					tab_neu.plus(z);
			}
//			Ausgabe.debug(tab_neu);

//			Fehler.zahl.wenn_Gleich(id_vater_text_bild.gLaenge(), tab_neu.gLaenge());
			if(ordner.gLaenge()==tab_neu.gLaenge()) { //Um einen Zyklus aufzulösen!
				if(this.zVaterUngueltig==null)
					Fehler.sonstige.da_ToDo("zVaterUngueltig muss gesetzt sein!");
				this.zVaterUngueltig.verarbeite(ordner);
				for(Ordner o : ordner)
					o.sVater(null);
			}

			ordner=tab_neu;
		}

//		TypTabelle2<Integer,Boolean> tt2=TypTabelle2.neu(Integer.class, Boolean.class);
//		for(Ordner o : kopie)
//			tt2.plus(o.id, o.offen);
//		sOffen(tt2);

		for(Ordner o : kopie) {
			boolean offen=o.offen;
			SWT_BaumFeld ast=this.id_map.gib((Integer)o.id);
			if(offen && ast.gKinderAnzahl()>0)
				ast.oeffnen();
			else
				ast.schliessen();
		}
	}

	public void sDragNDrop() {
		this.sDND_Quelle(new A_DND_Quelle(DNDART.VERSCHIEBEN, DNDART.LINK, DNDART.KOPIE, DNDTYP.MN_ID) {
			public boolean zielOk(A_SWT_Steuer<?,?> e) {
				Ausgabe.debug(e);
				TreeItem[] auswahl=t.swt().getSelection();
				return e==t && auswahl.length==1 && auswahl[0].getItemCount()==0; //Hier wird das Verschieben von Ordnern mit Kindern verhindert!
			}

			public Object uebergabe(DNDTYP typ, A_SWT_Steuer<?,?> e) {
				Ausgabe.debug(typ,e);
//				SWT_BaumFeld quell=(SWT_BaumFeld)e;
//				if(e!=t)
//					return null;
				TreeItem[] auswahl=t.swt().getSelection();
//				Ausgabe.debug(auswahl.length);
//				if(auswahl.length!=1 || auswahl[0].getItemCount()!=0)
//					return null;
				int schluessel=((SWT_BaumFeld)auswahl[0].getData()).schluessel.g();

				return new IDObjekt[]{new IDObjekt(t.getClass(), schluessel)};
			}
			public void abschluss(DNDTYP typ, DNDART art, A_SWT_Steuer<?, ?> e) {
			}
//			public void dragFinished(DragSourceEvent event) {
//				if (event.detail == DND.DROP_MOVE)
//					dragSourceItem[0].dispose();
//		        dragSourceItem[0] = null;
//	        }
		});
		this.sDND_Ziel(new A_DND_Ziel(DNDART.VERSCHIEBEN, DNDART.LINK, DNDART.KOPIE, DNDTYP.MN_ID) {
			public void aktion(DNDTYP typ, DNDART art, Object o, DropTargetEvent event) {
				Ausgabe.debug(typ, art, o, event.item, event.widget, event.detail, event.getSource(), event);
		        if(o == null) {
		        	event.detail = DND.DROP_NONE;
		            return;
		        }
		        IDObjekt[] idoa=(IDObjekt[])o;
		        IDObjekt ido=idoa[0];
		        TreeItem ziel_ti = (TreeItem) event.item;
		        if(!ido.klasse.equals(t.getClass())) { // FremdObject-Drop
		        	SWT_BaumFeld ziel = event.item==null ? null : (SWT_BaumFeld)ziel_ti.getData();
		        	Boolean b=t.externDrop.verarbeite(Gruppe2.neu(ziel, idoa));
		        	if(b!=null && !b)
		        		event.detail = DND.DROP_NONE;
		        	return;
		        }

		        if(idoa.length!=1) {
		        	event.detail = DND.DROP_NONE;
		            return;
		        }

		        int quell_id=ido.id;
				SWT_BaumFeld quell_bf=t.id_map.gib((Integer)quell_id);

				if(ziel_ti == null) {
					verschieben(quell_bf, quell_id, t, null);
		        } else {
		        	SWT_BaumFeld ziel_bf=(SWT_BaumFeld)ziel_ti.getData();
//					int ziel_id=(Integer)ziel_bf.schluessel.g();

		        	if(ziel_bf==quell_bf)
		        		return;

		            Point ziel_xy = t.swt().getDisplay().map(null, t.swt(), event.x, event.y);
		            Rectangle ziel_rahmen = ziel_ti.getBounds();

		            TreeItem ziel_vater_ti = ziel_ti.getParentItem();
		            if(ziel_vater_ti != null) {
		            	SWT_BaumFeld ziel_vater_bf = (SWT_BaumFeld)ziel_vater_ti.getData();
		            	TreeItem[] items = ziel_vater_ti.getItems();
		            	int index = 0;
		            	for(; index < items.length; index++)
		            		if(items[index] == ziel_ti)
		            			break;

		            	if(ziel_xy.y < ziel_rahmen.y + ziel_rahmen.height / 3)
		            		verschieben(quell_bf, quell_id, ziel_vater_bf, index);
		            	else if (ziel_xy.y > ziel_rahmen.y + 2 * ziel_rahmen.height / 3)
		            		verschieben(quell_bf, quell_id, ziel_vater_bf, index+1);
		            	else
		            		verschieben(quell_bf, quell_id, ziel_bf, null);
		            }
		            else {
		            	TreeItem[] items = t.swt().getItems();
		            	Integer index = 0;
		            	for(; index < items.length; index++)
			                if(items[index] == ziel_ti)
			                	break;

		            	if(ziel_xy.y < ziel_rahmen.y + ziel_rahmen.height / 3)
		            		verschieben(quell_bf, quell_id, t, index);
		            	else if(ziel_xy.y > ziel_rahmen.y + 2 * ziel_rahmen.height / 3)
		            		verschieben(quell_bf, quell_id, t, index+1);
		            	else
		            		verschieben(quell_bf, quell_id, ziel_bf, null);
		            }
	            }
				//Debug
				t.gN2M().ausgeben();
			}

			public void verschieben(SWT_BaumFeld quell, int quell_id, S_BaumFeld ziel, Integer index) {
				Integer vater_id=ziel instanceof SWT_BaumFeld ? (Integer)((SWT_BaumFeld)ziel).schluessel.g() : null;
				boolean ok= externVerschieben==null ? true : externVerschieben.verarbeite(Gruppe3.neu(quell_id, vater_id, index));
				if(!ok) return;
				SWT_BaumFeld neu_bf= index!=null
					? ziel.nBaumFeld(index, quell.gText())
					: ziel.nBaumFeld(quell.gText());

				S_Bild_SWT bild=null;
				try {
					bild = quell.gBild();
				}
				catch(F_Sys_Datei e) {
					Fehler.zeig(e);
				}
				if(bild!=null)
					neu_bf.sBild(bild);
				quell.freigeben(); //TODO bei der Quelle selbst machen!
				neu_bf.schluessel.s(quell_id);
				t.id_map.setze(quell_id, neu_bf);
				if(index==null && !ziel.istFreigegeben())
					ziel.oeffnen();
			}

			public void ueber(DNDTYP typ, DNDART art, DropTargetEvent event) {
		        if(art != DNDART.VERSCHIEBEN)
		        	return;

				event.feedback = DND.FEEDBACK_EXPAND | DND.FEEDBACK_SCROLL;
				if(event.item != null) {
					TreeItem item = (TreeItem) event.item;
		            Point pt = t.swt().getDisplay().map(null, swt(), event.x, event.y);
		            Rectangle bounds = item.getBounds();
		            if(pt.y < bounds.y + bounds.height / 3)
		            	event.feedback |= DND.FEEDBACK_INSERT_BEFORE;
		            else if(pt.y > bounds.y + 2 * bounds.height / 3)
		            	event.feedback |= DND.FEEDBACK_INSERT_AFTER;
		            else
		            	event.feedback |= DND.FEEDBACK_SELECT;
				}
        	}
		});
	}

	public S_Tabelle<Integer> gN2M() {
		S_Tabelle<Integer> erg=Tabelle.neu(2);
		S_Liste<SWT_BaumFeld> root=this.gKinder();
		for(SWT_BaumFeld feld : root) {
			int id=this.id_map.gSchluessel(feld);
			erg.plus(null, id);
			iGibN2M(erg, id, feld);
		}
		return erg;
	}

	public SWT_BaumFeld gFeld(int id) {
		return this.id_map.gib((Integer)id);
	}

	public SWT_BaumFeld gItemXY(int x, int y) {
		Point r=this.swt().toDisplay(0,0);
		TreeItem item=this.swt().getItem(new Point(x-r.x,y-r.y));
		return item==null ? null : (SWT_BaumFeld) item.getData();
	}

	public Integer gID(SWT_BaumFeld feld) {
		return this.id_map.gSchluessel(feld);
	}

	public Integer gID(Item item) {
		return gID((SWT_BaumFeld)item.getData());
	}

	// ERZEUGEN

	protected int style6() {
		t.s1_Rahmen(true);
		return 0;
	}

	protected void init6() {
	}

	// INTERN

	private void iGibN2M(S_Tabelle<Integer> ziel, int feld_id, SWT_BaumFeld feld) {
		S_Liste<SWT_BaumFeld> kinder=feld.gKinder();
		for(SWT_BaumFeld bf : kinder) {
			int id=this.id_map.gSchluessel(bf);
			ziel.plus(feld_id, id);
			iGibN2M(ziel, id, bf);
		}
	}

}
